
#include "Game.h"

NS_CC_BEGIN

unsigned int Actor::s_listIndex = 0;
Actor ** Actor::s_pList = new Actor * [200];
unsigned int Actor::s_snakeIndex = 0;
Actor ** Actor::s_pSnakes = new Actor * [50];
unsigned int Actor::s_enemyIndex = 0;
Actor ** Actor::s_pEnemies = new Actor * [50];

Actor::Actor()
	:m_pASprite(NULL)
	,m_pAnimate(NULL), m_anim(-1)
	,m_flags(0) ,m_color(ccWHITE) , m_opacity(255)
{
	s_pList[s_listIndex++] = this;
}

Actor* Actor::createWithASprite(ASprite *pASprite)
{
	Actor * actor = createWithTexture(pASprite->getTexture());
	actor->m_pASprite = pASprite;
	return actor;
}

Actor* Actor::createWithTexture(CCTexture2D *pTexture)
{
    Actor *pobSprite = new Actor();
    if (pobSprite && pobSprite->initWithTexture(pTexture))
    {
        pobSprite->autorelease();
        return pobSprite;
    }
    CC_SAFE_DELETE(pobSprite);
    return NULL;
}

Actor::~Actor()
{
	/*for (unsigned int i = 0; i < s_listIndex; i++) {
		if (s_pList[i] != this) {
			continue;
		}

		if (i + 1 < s_listIndex) {
			memcpy(&s_pList[i], &s_pList[i], sizeof(s_pList[i]) * (s_listIndex - i - 1));
		}

		s_listIndex--;
	}*/
	removeFromList(s_pList, s_listIndex);
}

void Actor::removeFromList(Actor **& pList, unsigned int & index)
{
	for (unsigned int i = 0; i < index; i++) {
		if (pList[i] != this) {
			continue;
		}

		if (i + 1 < index) {
			memcpy(&pList[i], &pList[i + 1], sizeof(pList[i]) * (index - i - 1));
		}

		index--;
	}
}

void Actor::setASprite(ASprite * pASprite)
{
	m_pASprite = pASprite;
	//initWithTexture(m_pASprite->getTexture(), 10);
	setTexture(pASprite->getTexture());
}

void Actor::setAnim(short anim)
{
	setAnim(anim, -1);
}

void Actor::setAnim(short anim, short loop)
{
	if (m_anim == anim) {
		if (m_pAnimate != NULL && !m_pAnimate->isDone()) {
			return;
		}
	}

	m_anim = anim;
	if (m_pAnimate != NULL) {
		stopAction(m_pAnimate);
		m_pAnimate = NULL;
	}

	if (loop <= 0) {
		m_pAnimate = runAction(CCRepeatForever::create(m_pASprite->getAnimate(anim)));
	} else {
		m_pAnimate = runAction(CCRepeat::create(m_pASprite->getAnimate(anim), loop));
	}
}

void Actor::setColor(ccColor3B color)
{
	m_color = color;
}

void Actor::follow(Actor & Actor)
{
	float dx = Actor.getPositionX() - getPositionX();
	float dy = Actor.getPositionY() - getPositionY();
	
	float distance = sqrt(dx * dx + dy * dy);
	float min = 16 * getScale();
	float moveDistance = distance - min;
	if (distance > min) {
		float ratio = (distance - min) / distance;
		moveTo(getPositionX() + dx * ratio, getPositionY() + dy * ratio);
	}
	update();
}

float Actor::getDistance(Actor & Actor)
{
	float dx = Actor.getPositionX() - getPositionX();
	float dy = Actor.getPositionY() - getPositionY();
	
	return sqrt(dx * dx + dy * dy);
}

void Actor::updateAll()
{
	s_pSnakes[0]->update();
	for (unsigned int i = 1; i < s_snakeIndex; i++) {
		s_pSnakes[i]->follow(*s_pSnakes[i - 1]);
	}

	for (unsigned int i = 0; i < s_enemyIndex; i++) {
		s_pEnemies[i]->update();
	}

	for (unsigned int i = 0; i < s_listIndex;) {
		if (s_pList[i]->getParent()) {
			i++;
		} else {
			s_pList[i]->removeAllChildrenWithCleanup(true);
			s_pList[i]->release();
		}
	}
}

void Actor::drawAll(CCLayer * layer)
{
	Actor ** pDrawList = new Actor *[s_listIndex];
	unsigned int drawListIdx = 0;
	
	for (unsigned int i = 0; i < s_listIndex; i++, drawListIdx++) {
		if (!s_pList[i]->getParent()) {
			continue;
		}

		pDrawList[drawListIdx] = s_pList[i];
		for (int j = drawListIdx; j > 0; j--) {
			if (pDrawList[j]->isBehind(pDrawList[j - 1])) {
				Actor * temp = pDrawList[j];
				pDrawList[j] = pDrawList[j - 1];
				pDrawList[j - 1] = temp;
			} else {
				break;
			}
		}
	}

	for (unsigned int i = 0; i < drawListIdx; i++) {
		CCNode * parent = pDrawList[i]->getParent();
		parent->removeChild(pDrawList[i], false);
//		pDrawList[i]->updateAnimation();
		parent->addChild(pDrawList[i]);
	}
}

// Flags
void Actor::addFlags(int flags)
{
	m_flags |= flags;
}

void Actor::removeFlags(int flags)
{
	m_flags &= ~flags;
}

bool Actor::checkFlags(int flags)
{
	return (m_flags & flags) != 0;
}

bool Actor::isBehind(Actor * actor)
{
	if (getPositionY() > actor->getPositionY()
			|| (getPositionY() == actor->getPositionY()
				&& getPositionX() > actor->getPositionX())) {
		return true;
	}

	return false;
}

void Actor::update() {}
void Actor::moveTo(float x, float y) {}
void Actor::setV(float vx, float vy) {}
float Actor::getVx() { return 0.0f; }
float Actor::getVy() { return 0.0f; }
void Actor::setVx(float vx) {}
void Actor::setVy(float vy) {}
void Actor::setAcc(float ax, float ay) {}
float Actor::getAx() { return 0.0f; }
float Actor::getAy() { return 0.0f; }
void Actor::setAx(float ax) {}
void Actor::setAy(float ay) {}

/////////////////////////////////////////////////////////////////////////////////////
// Snake

Snake::Snake()
	:m_vx(0) ,m_vy(0)
	,m_nextX(0) ,m_nextY(0)
	,m_hurtTimer(0)
	,m_blood(SNAKE_MAX_HP)
{
	//setScale(2.0);
	s_pSnakes[s_snakeIndex++] = this;
}

Snake* Snake::createWithASprite(ASprite *pASprite)
{
	Snake * snake = createWithTexture(pASprite->getTexture());
	snake->m_pASprite = pASprite;
	return snake;
}

Snake* Snake::createWithTexture(CCTexture2D *pTexture)
{
    Snake *pobSprite = new Snake();
    if (pobSprite && pobSprite->initWithTexture(pTexture))
    {
        pobSprite->autorelease();
        return pobSprite;
    }
    CC_SAFE_DELETE(pobSprite);
    return NULL;
}

Snake::~Snake()
{
	removeFromList(s_pSnakes, s_snakeIndex);
}

void Snake::update()
{
	if (m_hurtTimer > 0) {
		if ((--m_hurtTimer % 2) == 1) {
			setColor(ccRED);
			addFlags(CHANGE_COLOR);
		} else {
			setColor(ccWHITE);
			removeFlags(CHANGE_COLOR);
		}
	}

	if (this == Actor::s_pSnakes[0]) {
		if (getVx() == 0 && getVy() == 0) {
			CCSize size = getParent()->getContentSize();
			const float BOTTOM = 8, TOP = size.height - 8 * 3;
			const float LEFT = 8 * 2, RIGHT = size.width - LEFT;
			if (getPositionX() == LEFT || getPositionX() == RIGHT) {
				if (getPositionY() == TOP) {
					if (getPositionX() == LEFT) {

						moveTo(RIGHT, TOP);
					} else {
						moveTo(RIGHT, BOTTOM);
					}
				} else if (getPositionY() == BOTTOM) {
					if (getPositionX() == LEFT) {
						moveTo(LEFT, TOP);
					} else {
						moveTo(LEFT, BOTTOM);
					}
				} else if (getPositionY() > Actor::s_pList[1]->getPositionY()
						|| (Actor::s_pList[1]->getPositionY() == getPositionY() && (TOP - getPositionY() < getPositionY() - BOTTOM))) {
					moveTo(getPositionX(), TOP);
				} else if (getPositionY() != BOTTOM) {
					moveTo(getPositionX(), BOTTOM);
				}
			} else if (getPositionY() == TOP || getPositionY() == BOTTOM) {
				if (getPositionX() > Actor::s_pList[1]->getPositionX()
							|| (Actor::s_pList[1]->getPositionX() == getPositionX() && (RIGHT - getPositionX() < getPositionX() - LEFT))) {
					moveTo(RIGHT, getPositionY());
				} else {
					moveTo(LEFT, getPositionY());
				}
			}
		}
	}

	if (m_nextX > 0) {
		if (getPositionX() != m_nextX || getPositionY() != m_nextY) {
			m_dx = m_nextX;
			m_dy = m_nextY;
			m_vx = m_vy = 0;
			float disX = m_dx - getPositionX();
			float disY = m_dy - getPositionY();
			float distance = sqrtf((disX * disX) + (disY * disY));

			m_vx = MOVE_SPEED * disX / distance;
			m_vy = MOVE_SPEED * disY / distance;
		
			if (abs(m_vx) < abs(m_vy)) {
				setAnim(m_vy > 0 ? 0 : 2);
			} else {
				setAnim(m_vx > 0 ? 1 : 3);
			}
		}

		m_nextX = m_nextY = -1;
	}

	if (m_vx != 0) {
		setPositionX(getPositionX() + m_vx);
		if ((m_vx > 0 && getPositionX() >= m_dx)
				|| (m_vx < 0 && getPositionX() <= m_dx)) {
			setPositionX(m_dx);
			m_vx = 0;
		}
	}
	
	if (m_vy != 0) {
		setPositionY(getPositionY() + m_vy);
		if ((m_vy > 0 && getPositionY() >= m_dy)
				|| (m_vy < 0 && getPositionY() <= m_dy)) {
			setPositionY(m_dy);
			m_vy = 0;
		}
	}
}

void Snake::moveTo(float x, float y)
{
	m_nextX = x;
	m_nextY = y;
}

void Snake::follow(Actor & actor)
{
	float dx = actor.getPositionX() - getPositionX();
	float dy = actor.getPositionY() - getPositionY();
	
	float distance = sqrt(dx * dx + dy * dy);
	float min = 16 * getScale();
	float moveDistance = distance - min;
	if (distance > min) {
		float ratio = (distance - min) / distance;
		moveTo(getPositionX() + dx * ratio, getPositionY() + dy * ratio);
	}
	update();
}

void Snake::setV(float vx, float vy)
{
	setVx(vx);
	setVy(vy);
}

float Snake::getVx()
{
	return m_vx;
}

float Snake::getVy() {
	return m_vy;
}
void Snake::setVx(float vx)
{
	m_vx = vx;
}

void Snake::setVy(float vy)
{
	m_vy = vy;
}

void Snake::setAcc(float ax, float ay)
{
	setAx(ax);
	setAy(ay);
}

float Snake::getAx()
{
	return 0.0f;
}

float Snake::getAy()
{
	return 0.0f;
}

void Snake::setAx(float ax)
{
}

void Snake::setAy(float ay)
{
}

void Snake::takeDamage(int damage)
{
	m_blood = max(0, m_blood - damage);

	if (m_blood > 0) {
		m_hurtTimer = SNAKE_HURT_EFFECT_TIMER;
	} else {
		m_hurtTimer = 0;
		removeFlags(CHANGE_COLOR);
	}
}


/////////////////////////////////////////////////////////////////////////////////////
// Enemy

Enemy::Enemy()
	:m_blood(ENEMY_MAX_HP)
{
	//setScale(2.0);
	s_pEnemies[s_enemyIndex++] = this;
	setContentSize(CCSize(40, 40));
}

Enemy::~Enemy()
{
	removeFromList(s_pEnemies, s_enemyIndex);
}

Enemy* Enemy::createWithASprite(ASprite *pASprite)
{
	Enemy * enemy = createWithTexture(pASprite->getTexture());
	enemy->m_pASprite = pASprite;
	return enemy;
}

Enemy* Enemy::createWithTexture(CCTexture2D *pTexture)
{
    Enemy *pobSprite = new Enemy();
    if (pobSprite && pobSprite->initWithTexture(pTexture))
    {
        pobSprite->autorelease();
        return pobSprite;
    }
    CC_SAFE_DELETE(pobSprite);
    return NULL;
}

void Enemy::update()
{
	CCRect & rect = boundingBox();
	CCLog("x,y,x1,y1=[%f,%f,%f,%f]", rect.getMinX(), rect.getMinY(), rect.getMaxX(), rect.getMaxY());
	CCPoint point = ccp(Game::s_point.x / 2, Game::s_point.y / 2);
	if (CCRect::CCRectContainsPoint(rect, point)) {
		takeDamage(m_blood);
	}
	// Update enemy dead
	if (m_blood == 0) {
		if (m_deadTimer > 0) {
			setOpacity(255 * m_deadTimer / ENEMY_DEAD_TIMER);
			if (--m_deadTimer == 0) {
				getParent()->removeChild(this, false);
				addFlags(REMOVE);
			}
		}
		return;
	}
}

void Enemy::takeDamage(int damage)
{
	m_blood = max(0, m_blood - damage);

	if (m_blood == 0) {
		m_deadTimer = ENEMY_DEAD_TIMER;
		addFlags(CHANGE_OPACITY);
	}
}

bool Enemy::ccTouchesBegan(CCTouch *pTouches, CCEvent *pEvent)
{
    CCPoint pos = pTouches->getLocation();
	CCRect & rect = boundingBox();
	float dx = pos.x - getPositionX();
	float dy = pos.y - getPositionY();
	if (sqrt(dx * dx + dy * dy) < 16) {
		takeDamage(m_blood);
		return true;
	}

	return false;
}

NS_CC_END
